//////////////////////////////////////////////////////////////////////////////
//
//  TSLinkDrv.c : Link port driver library for TS101 processor
//
//
//  BEIJING ZHONGKEHAIXUN Science & Technology Co. Ltd.  
//  Copyright 2005 
//
//
//    Ver.  Dates     Author   Changes                                
//    ----  --------  -------  -----------------------------
//	  1.0   06/29/05  ktb&fxx   Create
//
//////////////////////////////////////////////////////////////////////////////


#include <defts101.h>
#include <builtins.h>
#include <sysreg.h>
#include <stdlib.h>
#include "../common/debug.h"

#define TS_EXTERN


#include "TSLinkDrv.h"

int trans_completed[4];


//////////////////////////////////////////////////////////////////////////////

void link_isr(int signo)
{
	int i;
    DEBUG("signo:%d",signo);

	if (signo >= SIGDMA8)
		i = signo - SIGDMA8;
	else
		i = signo - SIGDMA4;
    if (i != 1){
        LinkRecvDMA(1,&trans_completed[0],4);
    }
	trans_completed[i] = 1;
}

//////////////////////////////////////////////////////////////////////////////

int lstat_read (int num)
{
	switch (num)
	{
	default:
	case 0:return __builtin_sysreg_read(__LSTATC0);
	case 1:return __builtin_sysreg_read(__LSTATC1);
	case 2:return __builtin_sysreg_read(__LSTATC2);
	case 3:return __builtin_sysreg_read(__LSTATC3);
	}
}

__builtin_quad lbufrx_read (int num)
{
	switch (num)
	{
	default:
	case 0:return __builtin_sysreg_read4(__LBUFRX0);
	case 1:return __builtin_sysreg_read4(__LBUFRX1);
	case 2:return __builtin_sysreg_read4(__LBUFRX2);
	case 3:return __builtin_sysreg_read4(__LBUFRX3);
	}
}

void lbuftx_write (int num, __builtin_quad val)
{
	switch (num)
	{
	default:
	case 0:__builtin_sysreg_write4(__LBUFTX0, val);
		break;
	case 1:__builtin_sysreg_write4(__LBUFTX1, val);
		break;
	case 2:__builtin_sysreg_write4(__LBUFTX2, val);
		break;
	case 3:__builtin_sysreg_write4(__LBUFTX3, val);
		break;
	}
}

void lctl_write(int num, int val)
{
	switch (num)
	{
	default:
	case 0:__builtin_sysreg_write(__LCTL0, val);
		break;
	case 1:__builtin_sysreg_write(__LCTL1, val);
		break;
	case 2:__builtin_sysreg_write(__LCTL2, val);
		break;
	case 3:__builtin_sysreg_write(__LCTL3, val);
		break;
	}
}
void dc4_write(int num, __builtin_quad val)
{
	switch (num)
	{
	default:
	case 0:__builtin_sysreg_write4(__DC4, val);
		break;
	case 1:__builtin_sysreg_write4(__DC5, val);
		break;
	case 2:__builtin_sysreg_write4(__DC6, val);
		break;
	case 3:__builtin_sysreg_write4(__DC7, val);
		break;
	}
}
void dc8_write(int num, __builtin_quad val)
{
	switch (num)
	{
	default:
	case 0:__builtin_sysreg_write4(__DC8, val);
		break;
	case 1:__builtin_sysreg_write4(__DC9, val);
		break;
	case 2:__builtin_sysreg_write4(__DC10, val);
		break;
	case 3:__builtin_sysreg_write4(__DC11, val);
		break;
	}
}


//////////////////////////////////////////////////////////////////////////////
	
void LinkInit(int linkno, int speed)
{
	volatile __builtin_quad TCB_Clear;
    volatile int temp;
	
	TCB_Clear = __builtin_compose_128((long long)TCB_DISABLE << 32, 0);
	dc4_write(linkno, TCB_Clear);
	dc8_write(linkno, TCB_Clear);
	lctl_write(linkno, LCTL_DSBL);
	lstat_read(linkno);
	lctl_write(linkno,  LCTL_LREN | LCTL_PSIZE | LCTL_LTEN | speed);

	
    temp = __builtin_sysreg_read(__IMASKL);

    switch(linkno)
    {
    	case	0:	temp = temp | INT_DMA4 | INT_DMA8;
    				interrupt(SIGDMA4, link_isr);
    				interrupt(SIGDMA8, link_isr);
    				break;		  	
		    				
    	case	1:	temp = temp | INT_DMA5 | INT_DMA9;
    				interrupt(SIGDMA5, link_isr);
    				interrupt(SIGDMA9, link_isr);
    				break;		  	
		    				
    	case	2:	temp = temp | INT_DMA6 | INT_DMA10;
    				interrupt(SIGDMA6,  link_isr);
    				interrupt(SIGDMA10, link_isr);
    				break;		  	
    				
    	case	3:	temp = temp | INT_DMA7 | INT_DMA11;
    				interrupt(SIGDMA7,  link_isr);
    				interrupt(SIGDMA11, link_isr);
    				break;		  	
    	
    }
                  

    __builtin_sysreg_write(__IMASKL, temp);
    
	//Enable globle Interrupt
    temp = __builtin_sysreg_read(__IMASKH);	
    temp = temp | INT_GIE;
    __builtin_sysreg_write(__IMASKH, temp);

		
    trans_completed[linkno] = 0;

}

//////////////////////////////////////////////////////////////////////////////

int IsLinkDMADone(int linkno)
{
    // Wait for the DMA to complete
	return	trans_completed[linkno];
}

//////////////////////////////////////////////////////////////////////////////

void LinkRecvDMA(int linkno, void* DestAddr, unsigned int nWords)
{
    volatile __builtin_quad TCB_Clear;
		
    unsigned long *pReadData  =
    	(unsigned long *)(DestAddr);
	__builtin_quad *pQData =
    	(__builtin_quad *)(DestAddr);

/*    	
    // If we're reading a quadword and the rx buffer is full...
    //
  	if (sizeof(__builtin_quad) == nWords &&
  		0 != (lstat_read(linkno) & 0x8))
  	{
  		// ...then we can just read the qword directly from
  		// the linkport and not have to worry about DMA.
  		*pQData = lbufrx_read(linkno); //lbufrx_read(linkNo);
  		
  		return sizeof(__builtin_quad);
  	}
*/

    // Clear DMA complete flag
	trans_completed[linkno] = 0;

  	// Set up the DMA for the transfer
  	//
#pragma align 16
		__builtin_quad quadWord;
		
    // Set up Rx DMA
    union
    {
    	unsigned long vWord[4];
    } dma;
    
    dma.vWord[0] = (unsigned long) pReadData;
    dma.vWord[1] = (nWords << 16) | 4;
    dma.vWord[2] = 0;

    if((unsigned int)DestAddr > 0x003fffff)	// external memory
    dma.vWord[3] = TCB_EXTMEM | TCB_QUAD | TCB_INT ;//| TCB_DMAR;
    else									// internal memory
    dma.vWord[3] = TCB_INTMEM | TCB_QUAD | TCB_INT ;//| TCB_DMAR;
    
    memcpy (&quadWord, &dma, sizeof(dma));
    
	TCB_Clear = __builtin_compose_128((long long)TCB_DISABLE << 32, 0);
	dc8_write(linkno, TCB_Clear);
	dc8_write(linkno, quadWord);
}

//////////////////////////////////////////////////////////////////////////////

void LinkRecvDMAW(int linkno, void* DestAddr, unsigned int nWords)
{
	
	LinkRecvDMA(linkno, DestAddr, nWords);
	
    // Wait for the DMA to complete
	while(!IsLinkDMADone(linkno));
}

void LinkRecv2DDMA(int linkno, void* DestAddr, unsigned int nCol, unsigned int nRow)
{
    volatile __builtin_quad TCB_Clear;
		
    unsigned long *pReadData  =
    	(unsigned long *)(DestAddr);
	__builtin_quad *pQData =
    	(__builtin_quad *)(DestAddr);

/*    	
    // If we're reading a quadword and the rx buffer is full...
    //
  	if (sizeof(__builtin_quad) == nWords &&
  		0 != (lstat_read(linkno) & 0x8))
  	{
  		// ...then we can just read the qword directly from
  		// the linkport and not have to worry about DMA.
  		*pQData = lbufrx_read(linkno); //lbufrx_read(linkNo);
  		
  		return sizeof(__builtin_quad);
  	}
*/

    // Clear DMA complete flag
	trans_completed[linkno] = 0;

  	// Set up the DMA for the transfer
  	//
#pragma align 16
		__builtin_quad quadWord;
		
    // Set up Rx DMA
    union
    {
    	unsigned long vWord[4];
    } dma;
    
    dma.vWord[0] = (unsigned long) pReadData;
    dma.vWord[1] = (nCol << 16) | 4;
    dma.vWord[2] = (nRow << 16) | 4;

    if((unsigned int)DestAddr > 0x003fffff)	// external memory
    dma.vWord[3] = TCB_EXTMEM | TCB_QUAD | TCB_INT | TCB_TWODIM;//| TCB_DMAR;
    else									// internal memory
    dma.vWord[3] = TCB_INTMEM | TCB_QUAD | TCB_INT | TCB_TWODIM;//| TCB_DMAR;
    
    memcpy (&quadWord, &dma, sizeof(dma));
    
	TCB_Clear = __builtin_compose_128((long long)TCB_DISABLE << 32, 0);
	dc8_write(linkno, TCB_Clear);
	dc8_write(linkno, quadWord);
}

//////////////////////////////////////////////////////////////////////////////

void LinkRecv2DDMAW(int linkno, void* DestAddr, unsigned int nCol, unsigned int nRow)
{
	
	LinkRecv2DDMA(linkno, DestAddr, nCol, nRow);
	
    // Wait for the DMA to complete
	while(!IsLinkDMADone(linkno));
}

//////////////////////////////////////////////////////////////////////////////

void LinkSendDMA(int linkno, void* SrcAddr, unsigned int nWords)
{
    volatile __builtin_quad TCB_Clear;

    unsigned long *pWriteData =
    	(unsigned long *)SrcAddr;
	__builtin_quad *pQData =
    	(__builtin_quad *)SrcAddr;

/*    	
    // If we're writing a quadword and the tx buffer is empty...
    //
  	if (sizeof(__builtin_quad) == nWords &&
  		 0 == (lstat_read(linkno) & LSTAT_TST))
  	{
  		// ...then we can just write the qword directly to
  		// the linkport and not have to worry about DMA.
  		lbuftx_write(linkno, *pQData); //lbuftx_write(linkNo, *pQData);
  		
  		return sizeof(__builtin_quad);
  	}
*/
  	
    // Clear DMA complete flag
	trans_completed[linkno] = 0;

  	// Set up the DMA for the transfer
  	//
#pragma align 16
   		__builtin_quad quadWord;
	
    // Set up tx DMA
    static union
    {
    	unsigned long vWord[4];
    } dma;
    
    dma.vWord[0] = (unsigned long)pWriteData;
    dma.vWord[1] = (nWords << 16) | 4;
    dma.vWord[2] = 0;

    if((unsigned int)SrcAddr > 0x003fffff)	// external memory
    dma.vWord[3] = TCB_EXTMEM | TCB_QUAD | TCB_INT ;//| TCB_DMAR;
    else									// internal memory
    dma.vWord[3] = TCB_INTMEM | TCB_QUAD | TCB_INT ;//| TCB_DMAR;
    
    
    memcpy (&quadWord, &dma, sizeof(dma));
 	TCB_Clear = __builtin_compose_128((long long)TCB_DISABLE << 32, 0);
	dc4_write(linkno, TCB_Clear);
   
	dc4_write(linkno, quadWord);
    
}

//////////////////////////////////////////////////////////////////////////////

void LinkSendDMAW(int linkno, void* SrcAddr, unsigned int nWords)
{
	LinkSendDMA(linkno, SrcAddr, nWords);
	
    // Wait for the DMA to complete
	while(!IsLinkDMADone(linkno));
}

void LinkSend2DDMA(int linkno, void* SrcAddr, unsigned int nCol, unsigned int nRow)
{
    volatile __builtin_quad TCB_Clear;

    unsigned long *pWriteData =
    	(unsigned long *)SrcAddr;
	__builtin_quad *pQData =
    	(__builtin_quad *)SrcAddr;

/*    	
    // If we're writing a quadword and the tx buffer is empty...
    //
  	if (sizeof(__builtin_quad) == nWords &&
  		 0 == (lstat_read(linkno) & LSTAT_TST))
  	{
  		// ...then we can just write the qword directly to
  		// the linkport and not have to worry about DMA.
  		lbuftx_write(linkno, *pQData); //lbuftx_write(linkNo, *pQData);
  		
  		return sizeof(__builtin_quad);
  	}
*/
  	
    // Clear DMA complete flag
	trans_completed[linkno] = 0;

  	// Set up the DMA for the transfer
  	//
#pragma align 16
   		__builtin_quad quadWord;
	
    // Set up tx DMA
    static union
    {
    	unsigned long vWord[4];
    } dma;
    
    dma.vWord[0] = (unsigned long)pWriteData;
    dma.vWord[1] = (nCol << 16) | 4;
    dma.vWord[2] = (nRow << 16) | 4;

    if((unsigned int)SrcAddr > 0x003fffff)	// external memory
    dma.vWord[3] = TCB_EXTMEM | TCB_QUAD | TCB_INT | TCB_TWODIM ;//| TCB_DMAR;
    else									// internal memory
    dma.vWord[3] = TCB_INTMEM | TCB_QUAD | TCB_INT | TCB_TWODIM ;//| TCB_DMAR;
    
    
    memcpy (&quadWord, &dma, sizeof(dma));
 	TCB_Clear = __builtin_compose_128((long long)TCB_DISABLE << 32, 0);
	dc4_write(linkno, TCB_Clear);
   
	dc4_write(linkno, quadWord);
    
}

//////////////////////////////////////////////////////////////////////////////

void LinkSend2DDMAW(int linkno, void* SrcAddr, unsigned int nCol, unsigned int nRow)
{
	LinkSend2DDMA(linkno, SrcAddr, nCol, nRow);
	
    // Wait for the DMA to complete
	while(!IsLinkDMADone(linkno));
}


//////////////////////////////////////////////////////////////////////////////

void LinkSendCore(int linkno, void* SrcAddr, unsigned int nWords)
{
	unsigned int i;
	
	__builtin_quad *pQData =
    	(__builtin_quad *)SrcAddr;
  	for(i = 0; i < nWords / sizeof(__builtin_quad); i++)
  	{
  		while(0 != (lstat_read(linkno) & LSTAT_TST)){}
  		lbuftx_write(linkno, *(pQData+i));
  	}
}

//////////////////////////////////////////////////////////////////////////////


void LinkRecvCore(int linkno, void* DestAddr, unsigned int nWords)
{
	unsigned int i;

	__builtin_quad *pQData =
    	(__builtin_quad *)(DestAddr);


  	for(i = 0; i < nWords / sizeof(__builtin_quad); i++)
	{  	
  	
  	 	while(0 == (lstat_read(linkno) & 0x8)){}
        *(pQData+i) = lbufrx_read(linkno);
	}
}

//////////////////////////////////////////////////////////////////////////////

